import static org.lwjgl.opengl.GL11.GL_AMBIENT_AND_DIFFUSE;
import static org.lwjgl.opengl.GL11.GL_BACK;
import static org.lwjgl.opengl.GL11.GL_BLEND;
import static org.lwjgl.opengl.GL11.GL_COLOR_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_COLOR_MATERIAL;
import static org.lwjgl.opengl.GL11.GL_CULL_FACE;
import static org.lwjgl.opengl.GL11.GL_DEPTH_BUFFER_BIT;
import static org.lwjgl.opengl.GL11.GL_DEPTH_TEST;
import static org.lwjgl.opengl.GL11.GL_DIFFUSE;
import static org.lwjgl.opengl.GL11.GL_FRONT;
import static org.lwjgl.opengl.GL11.GL_LIGHT0;
import static org.lwjgl.opengl.GL11.GL_LIGHTING;
import static org.lwjgl.opengl.GL11.GL_LIGHT_MODEL_AMBIENT;
import static org.lwjgl.opengl.GL11.GL_LINEAR_ATTENUATION;
import static org.lwjgl.opengl.GL11.GL_MODELVIEW;
import static org.lwjgl.opengl.GL11.GL_ONE_MINUS_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_POSITION;
import static org.lwjgl.opengl.GL11.GL_PROJECTION;
import static org.lwjgl.opengl.GL11.GL_PROJECTION_MATRIX;
import static org.lwjgl.opengl.GL11.GL_QUADRATIC_ATTENUATION;
import static org.lwjgl.opengl.GL11.GL_SHININESS;
import static org.lwjgl.opengl.GL11.GL_SMOOTH;
import static org.lwjgl.opengl.GL11.GL_SPECULAR;
import static org.lwjgl.opengl.GL11.GL_SRC_ALPHA;
import static org.lwjgl.opengl.GL11.GL_TEXTURE_2D;
import static org.lwjgl.opengl.GL11.glBlendFunc;
import static org.lwjgl.opengl.GL11.glCallList;
import static org.lwjgl.opengl.GL11.glClear;
import static org.lwjgl.opengl.GL11.glClearColor;
import static org.lwjgl.opengl.GL11.glColor3f;
import static org.lwjgl.opengl.GL11.glColorMaterial;
import static org.lwjgl.opengl.GL11.glCullFace;
import static org.lwjgl.opengl.GL11.glDeleteLists;
import static org.lwjgl.opengl.GL11.glDisable;
import static org.lwjgl.opengl.GL11.glEnable;
import static org.lwjgl.opengl.GL11.glGetFloat;
import static org.lwjgl.opengl.GL11.glLight;
import static org.lwjgl.opengl.GL11.glLightModel;
import static org.lwjgl.opengl.GL11.glLightf;
import static org.lwjgl.opengl.GL11.glLoadIdentity;
import static org.lwjgl.opengl.GL11.glLoadMatrix;
import static org.lwjgl.opengl.GL11.glMaterial;
import static org.lwjgl.opengl.GL11.glMaterialf;
import static org.lwjgl.opengl.GL11.glMatrixMode;
import static org.lwjgl.opengl.GL11.glOrtho;
import static org.lwjgl.opengl.GL11.glPopMatrix;
import static org.lwjgl.opengl.GL11.glPushMatrix;
import static org.lwjgl.opengl.GL11.glRotated;
import static org.lwjgl.opengl.GL11.glShadeModel;
import static org.lwjgl.opengl.GL11.glTranslated;
import static org.lwjgl.opengl.GL20.glDeleteProgram;
import static org.lwjgl.opengl.GL20.glUseProgram;
import static org.lwjgl.util.glu.GLU.gluPerspective;

import java.awt.Color;
import java.awt.Font;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.net.UnknownHostException;
import java.nio.FloatBuffer;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Random;
import java.util.StringTokenizer;

import org.lwjgl.BufferUtils;
import org.lwjgl.LWJGLException;
import org.lwjgl.Sys;
import org.lwjgl.input.Keyboard;
import org.lwjgl.input.Mouse;
import org.lwjgl.opengl.Display;
import org.lwjgl.util.glu.Sphere;
import org.newdawn.slick.SlickException;
import org.newdawn.slick.UnicodeFont;
import org.newdawn.slick.font.effects.ColorEffect;
import org.newdawn.slick.opengl.Texture;
import org.newdawn.slick.opengl.TextureLoader;

public class Environment
{
	private final double PLAYER_ACCEL = 3.8;
	private final double FRICTION = 1.35;
	private final double MOUSE_SPEED = 0.1;
	private final double GRAVITY = 0.8;
	private final double MAX_HEIGHT = 7500;
	private final double MIN_DISTANCE_FROM_BALL = 100;
	private final float BALL_SIZE = 50;
	private final int BALL_RESOLUTION = 20;
	private final int SCREEN_X = 2880, SCREEN_Y = 1800;
	private final double JUMP_VELOCITY = 15;
	private final float[] AMBIENT_LIGHT_COLOR = {0.05f, 0.05f, 0.05f, 0.05f};
	private final float[] DIFFUSE_LIGHT_COLOR = {20.0f, 20.0f, 20.0f, 1.0f};
	private final float[] SPECULAR_LIGHT_COLOR = {1.0f, 1.0f, 1.0f, 1.0f};
	private final float[] SPECULAR_HIGHLIGHT_COLOR = {1.0f, 1.0f, 1.0f, 1.0f};
	private final float SHININESS = 50.0f;
	private final float LINEAR_LIGHT_ATTENUATION = 0.005f;
	private final float QUADRATIC_LIGHT_ATTENUATION = 0.0000005f;
	private final int TEXT_OFFSET = 20;
	private final int SCORE_TAKEN_FROM_FALLING = 100;
	private final int SCORE_ADDED = 25;
	private final double DEFAULT_FRAME_LENGTH = .016666;
	
	private int basicLightShader;
	private float[] lightPosition = {0.0f, 0.0f, 0.0f, 1.0f};
	private boolean gKeyPressed = false, spacePressed = false;
	private boolean touchingBottom = false;
	private boolean gravityEnabled = true;
	private double playerX, playerY, playerZ;
	private double velocityX, velocityY, velocityZ;
	private double playerTryMovementX, playerTryMovementY, playerTryMovementZ;
	private double rotationX, rotationY;
	private ArrayList<Prism> mapCollision = new ArrayList<Prism>();
	private Prism playerCollisionBox;
	private int levelList;
	private int skyboxList;
	private int crosshairList;
	private boolean userExits = false;
	private Texture defaultWoodTexture;
	private Texture skyboxTexture;
	private int score = 0;
	private double spawnX = 0, spawnY = 0, spawnZ = 0;
	private ArrayList<BlockType.BallFlag> balls = new ArrayList<BlockType.BallFlag>();
	private double opponentX, opponentY, opponentZ;

	private FloatBuffer perspectiveMatrix = BufferUtils.createFloatBuffer(16); //For switching back into perspective
	private FloatBuffer orthographicMatrix = BufferUtils.createFloatBuffer(16); //For changing into orthographic mode for font rendering
	private UnicodeFont font;
	private DecimalFormat defaultFormatter = new DecimalFormat("#.##");
	
	private Random ballChooser = new Random();
	private int chosenBall = 0;
	private boolean showText = false;
	
	private long startTime = 0;
	private long curTime = 0;
	private long lastTime = 0;
	private double frameLength = 0;
	
	Socket defaultServerSocket;
	BufferedReader socketReader;
	PrintWriter socketWriter;
	
	public enum CollisionType
	{
		PLAYER, STANDARD, STEP, NONE;
	}
	
	public static void main(String[] args)
	{
		new Environment();
	}

	public Environment()
	{
//		try
//		{
//			setUpNetwork();
//		}
//		catch (IOException e)
//		{
//			e.printStackTrace();
//		}
		setUpGL();
		setUpCollision();
		setUpScene();
		createShaders();
		setUpFont();
		chooseBall();
		loop();
		cleanUp();
	}
	
	public void setUpGL()
	{
		try
		{
			Display.setTitle("Test");
			Display.setVSyncEnabled(true);
			Display.create();
			Display.setFullscreen(true);
		}
		catch(LWJGLException e)
		{
			e.printStackTrace();
		}

		Mouse.setGrabbed(true);

		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		gluPerspective(45.0f, (float) SCREEN_X / SCREEN_Y, .1f, 150000);
		
		glGetFloat(GL_PROJECTION_MATRIX, perspectiveMatrix);
		glMatrixMode(GL_PROJECTION);
		glLoadIdentity();
		glOrtho(0, Display.getWidth(), Display.getHeight(), 0, 1, -1);
		glGetFloat(GL_PROJECTION_MATRIX, orthographicMatrix);
		glLoadMatrix(perspectiveMatrix);
		
		glMatrixMode(GL_MODELVIEW);
		glEnable(GL_DEPTH_TEST);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_CULL_FACE);
		glCullFace(GL_BACK);
		
		glEnable(GL_BLEND);
		glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
		
		//Lighting initialization below
		glShadeModel(GL_SMOOTH);
		glLightModel(GL_LIGHT_MODEL_AMBIENT, BufferHelper.makeBuffer(AMBIENT_LIGHT_COLOR));
		glLight(GL_LIGHT0, GL_POSITION, BufferHelper.makeBuffer(lightPosition));
		glLight(GL_LIGHT0, GL_DIFFUSE, BufferHelper.makeBuffer(DIFFUSE_LIGHT_COLOR));
		glLight(GL_LIGHT0, GL_SPECULAR, BufferHelper.makeBuffer(SPECULAR_LIGHT_COLOR));
		glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, LINEAR_LIGHT_ATTENUATION);
		glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, QUADRATIC_LIGHT_ATTENUATION);
		glMaterial(GL_FRONT, GL_SPECULAR, BufferHelper.makeBuffer(SPECULAR_HIGHLIGHT_COLOR));
		glMaterialf(GL_FRONT, GL_SHININESS, SHININESS);
		glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
		
		glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
		
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		
		try
		{
			createTextures();
		}
		catch (IOException e)
		{
			e.printStackTrace();
		}
	}

	public void setUpNetwork() throws UnknownHostException, IOException
	{
		defaultServerSocket = new Socket("172.30.24.154", 5000);
		socketReader = new BufferedReader(new InputStreamReader(defaultServerSocket.getInputStream()));
		socketWriter = new PrintWriter(defaultServerSocket.getOutputStream());
	}
	
	public void updateNetwork() throws IOException
	{
		String curOtherDistance = socketReader.readLine();
		socketWriter.write(playerX + " " + playerY + " " + playerZ);
		StringTokenizer opponent = new StringTokenizer(curOtherDistance);
		opponentX = Double.parseDouble(opponent.nextToken());
		opponentY = Double.parseDouble(opponent.nextToken());
		opponentZ = Double.parseDouble(opponent.nextToken());
	}
	
	public void setUpScene()
	{
		loadMap("map", 5, 200.0, 200.0, 200.0);
		skyboxList = DisplayListHelper.makeDisplayList(new ObjectCoords("skybox"));
		crosshairList = DisplayListHelper.makeDisplayList(new ObjectCoords("crosshair"));
		
	}
	
	@SuppressWarnings("unchecked")
	public void setUpFont()
	{
		Font defaultFont = new Font("Arial", Font.BOLD, 48);
		font = new UnicodeFont(defaultFont);
		font.getEffects().add(new ColorEffect(Color.BLACK));
		font.addAsciiGlyphs();
		try
		{
			font.loadGlyphs();
		}
		catch (SlickException e)
		{
			e.printStackTrace();
		}
	}
	
	public void chooseBall()
	{
		int chosen = ballChooser.nextInt(balls.size());
		for(; chosen == chosenBall; chosen = ballChooser.nextInt(balls.size()));
		chosenBall = chosen;
	}
	
	public void createShaders()
	{
		basicLightShader = ShaderHelper.createShaderProgram("phongLighting");
	}
	
	public void createTextures() throws FileNotFoundException, IOException
	{
		defaultWoodTexture = TextureLoader.getTexture("JPG", new FileInputStream(new File("collisionBoxTexture.jpg")));
		skyboxTexture = TextureLoader.getTexture("PNG", new FileInputStream(new File("skybox.png")));
	}

	public void setUpCollision()
	{
		playerCollisionBox = new PrismImporter("player").getPrism(true);
	}

	public void loop()
	{
		startTime = Sys.getTime();
		while(!Display.isCloseRequested() && !userExits)
		{
//			try
//			{
//				updateNetwork();
//			}
//			catch (IOException e)
//			{
//				e.printStackTrace();
//			}
			glLoadIdentity();
			glRotated(rotationY, 1.0f, 0.0f, 0.0f);
			glRotated(rotationX, 0.0f, 1.0f, 0.0f);
			glTranslated(playerX, playerY, playerZ);
			checkCollisionForPlayer();
			
			lightPosition[0] = (float) -playerX;
			lightPosition[1] = (float) -playerY;
			lightPosition[2] = (float) -playerZ;
			
//			lightPosition[0] = (float) velocityX;
//			lightPosition[1] = (float) velocityY;
//			lightPosition[2] = (float) velocityZ;
			
			glLight(GL_LIGHT0, GL_POSITION, BufferHelper.makeBuffer(lightPosition)); //Light movement
			render();
			
			updateTime();
			getInput();
			Display.update();
			Display.sync(60);
		}
	}
	
	public void updateTime()
	{
		lastTime = curTime;
		curTime = Sys.getTime();
		frameLength = (curTime - lastTime) / 1000.0;
	}
	
	public void checkCollisionForPlayer()
	{
		for(int i = 0; i < mapCollision.size(); i++)
		{
			CollisionData curData = CollisionHelper.checkCollision(mapCollision.get(i), playerCollisionBox, playerTryMovementX, playerTryMovementY, playerTryMovementZ);
			if(Math.abs(playerTryMovementX) > Math.abs(curData.x)) playerTryMovementX = curData.x;
			if(Math.abs(playerTryMovementY) > Math.abs(curData.y)) playerTryMovementY = curData.y;
			if(Math.abs(playerTryMovementZ) > Math.abs(curData.z)) playerTryMovementZ = curData.z;
			if(curData.touchingBottom) touchingBottom = true;
		}
		
		playerCollisionBox.move(playerTryMovementX, playerTryMovementY, playerTryMovementZ);
		
		playerX -= playerTryMovementX;
		playerY -= playerTryMovementY;
		playerZ -= playerTryMovementZ;
		velocityX = -playerTryMovementX;
		velocityY = -playerTryMovementY;
		velocityZ = -playerTryMovementZ;
		
		if(playerY > MAX_HEIGHT)
		{
			playerCollisionBox.move(playerX - spawnX, playerY - spawnY, playerZ - spawnZ);
			playerX = spawnX;
			playerY = spawnY;
			playerZ = spawnZ;
			velocityX = 0;
			velocityY = 0;
			velocityZ = 0;
			score -= SCORE_TAKEN_FROM_FALLING;
		}
		
		if(Math.sqrt((playerX + balls.get(chosenBall).position.getX()) * (playerX + balls.get(chosenBall).position.getX()) +
				     (playerY + balls.get(chosenBall).position.getY()) * (playerY + balls.get(chosenBall).position.getY()) +
				     (playerZ + balls.get(chosenBall).position.getZ()) * (playerZ + balls.get(chosenBall).position.getZ())) < MIN_DISTANCE_FROM_BALL)
		{
			score += SCORE_ADDED;
			chooseBall();
		}
	}

	public void getInput()
	{
		double frameMultiplier = frameLength / DEFAULT_FRAME_LENGTH;
		
		if(Keyboard.isKeyDown(Keyboard.KEY_ESCAPE))
		{
			userExits = true;
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_W))
		{
			velocityZ += Math.sin((rotationX + 90) * Math.PI / 180) * PLAYER_ACCEL;
			velocityX += Math.cos((rotationX + 90) * Math.PI / 180) * PLAYER_ACCEL;
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_A))
		{
			velocityX += Math.cos(rotationX * Math.PI / 180) * PLAYER_ACCEL;
			velocityZ += Math.sin(rotationX * Math.PI / 180) * PLAYER_ACCEL;
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_S))
		{
			velocityZ -= Math.sin((rotationX + 90) * Math.PI / 180) * PLAYER_ACCEL;
			velocityX -= Math.cos((rotationX + 90) * Math.PI / 180) * PLAYER_ACCEL;
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_D))
		{
			velocityX -= Math.cos(rotationX * Math.PI / 180) * PLAYER_ACCEL;
			velocityZ -= Math.sin(rotationX * Math.PI / 180) * PLAYER_ACCEL;
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_G) && !gKeyPressed)
		{
			if(gravityEnabled) gravityEnabled = false;
			else gravityEnabled = true;
			gKeyPressed = true;
		}
		else
		{
			gKeyPressed = false;
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_SPACE) && touchingBottom && !spacePressed)
		{
			velocityY -= JUMP_VELOCITY;
			spacePressed = true;
		}
		else
		{
			spacePressed = false;
		}
		if(Keyboard.isKeyDown(Keyboard.KEY_LSHIFT))
		{
			showText = true;
		}
		else
		{
			showText = false;
		}

		touchingBottom = false;
		
		rotationX += Mouse.getDX() * MOUSE_SPEED * frameMultiplier;
		rotationY -= Mouse.getDY() * MOUSE_SPEED * frameMultiplier;
		
		if(rotationY > 90)
		{
			rotationY = 90;
		}
		else if(rotationY < -90)
		{
			rotationY = -90;
		}

		if(gravityEnabled) velocityY += GRAVITY /** frameMultiplier*/;
		else velocityY = 0;
		
		velocityX /= FRICTION;
		velocityZ /= FRICTION;
		
		playerTryMovementX = -velocityX * frameMultiplier; //For frame independent movement
		playerTryMovementY = -velocityY * frameMultiplier;
		playerTryMovementZ = -velocityZ * frameMultiplier;
	}

	public void render()
	{
		glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glColor3f(1.0f, 1.0f, 1.0f);
		
		glDisable(GL_LIGHTING);
//		glUseProgram(basicLightShader);
		defaultWoodTexture.bind();
		glCallList(levelList);
//		glUseProgram(0);
		glEnable(GL_LIGHTING);
		
//		glDisable(GL_LIGHTING);
//		glDisable(GL_TEXTURE_2D);
//		glTranslated(opponentX, opponentY, opponentZ);
//		Sphere opponent = new Sphere();
//		glColor3f(1.0f, 0.0f, 0.0f);
//		opponent.draw(BALL_SIZE, BALL_RESOLUTION, BALL_RESOLUTION);
//		glEnable(GL_TEXTURE_2D);
//		glEnable(GL_LIGHTING);
		
		glDisable(GL_LIGHTING);
		skyboxTexture.bind();
		glPushMatrix();
		glTranslated(-playerX, -playerY, -playerZ);
		glCallList(skyboxList);
		glPopMatrix();
		glEnable(GL_LIGHTING);
		
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glPushMatrix();
		glLoadIdentity();
		glCallList(crosshairList);
		glPopMatrix();
		glEnable(GL_LIGHTING);
		glEnable(GL_TEXTURE_2D);
		
		glDisable(GL_LIGHTING);
		glDisable(GL_TEXTURE_2D);
		glEnable(GL_COLOR_MATERIAL);
		glColor3f(0.0f, 1.0f, 0.0f);
		glPushMatrix();
		glTranslated(balls.get(chosenBall).position.getX(), balls.get(chosenBall).position.getY() - BALL_SIZE, balls.get(chosenBall).position.getZ());
		Sphere s = new Sphere();
		s.draw(BALL_SIZE, BALL_RESOLUTION, BALL_RESOLUTION);
		glPopMatrix();
		glDisable(GL_COLOR_MATERIAL);
		glEnable(GL_TEXTURE_2D);
		glEnable(GL_LIGHTING);
		
		renderFont();
	}

	public void renderFont()
	{
		glMatrixMode(GL_PROJECTION);
		glLoadMatrix(orthographicMatrix);
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();
		glDisable(GL_LIGHTING);
		font.drawString(TEXT_OFFSET, TEXT_OFFSET, "Score: " + score);
		if(showText)
		{
			font.drawString(TEXT_OFFSET, 4 * TEXT_OFFSET, "Position X: " + defaultFormatter.format(-playerX) + ", Y: " + defaultFormatter.format(-playerY) + ", Z: " + defaultFormatter.format(-playerZ));
			font.drawString(TEXT_OFFSET, 7 * TEXT_OFFSET, "Ball X: " + defaultFormatter.format(balls.get(chosenBall).position.getX()) + ", Y: " + defaultFormatter.format(balls.get(chosenBall).position.getY()) + ", Z: " + defaultFormatter.format(balls.get(chosenBall).position.getZ()));
			font.drawString(TEXT_OFFSET, 10 * TEXT_OFFSET, "Time Elapsed: " + defaultFormatter.format(((curTime - startTime) / 1000.0)));
			font.drawString(TEXT_OFFSET, 13 * TEXT_OFFSET, "Frame rate: " + (int) (1 / frameLength) + ", Frame time: " + frameLength); //Currently oscillating between 58 and 62
		}
		glEnable(GL_LIGHTING);
		glPopMatrix();
		glMatrixMode(GL_PROJECTION);
		glLoadMatrix(perspectiveMatrix);
		glMatrixMode(GL_MODELVIEW);
	}
	
	public void cleanUp()
	{
		glDeleteLists(levelList, 1);
		glDeleteLists(skyboxList, 1);
		glDeleteProgram(basicLightShader);
		Display.destroy();
		System.exit(0);
	}

	public void loadMap(String mapName, int numLevels, double sizeX, double sizeY, double sizeZ) //Block size, not map size
	{
		levelList = DisplayListHelper.startList();
		for(int i = 0; i < numLevels; i++)
		{
			MapImporter mi = new MapImporter(mapName + i + ".map");
			for(int j = 0; j < mi.sizeX(); j++)
			{
				for(int k = 0; k < mi.sizeY(); k++)
				{
					if(mi.getObject(j, k) instanceof BlockType.StandardBlock)
					{
						Prism prism = new Prism(j * sizeX, i * sizeY, k * sizeZ, sizeX, sizeY, sizeZ, false);
						mapCollision.add(prism);
						DisplayListHelper.dispForPrism(new PrismImporter("baseBlock", prism));
					}
					else if(mi.getObject(j, k) instanceof BlockType.SpawnBlock)
					{
						playerX = -(j * sizeX + sizeX / 2);
						playerY = -(i * sizeY + sizeY / 2);
						playerZ = -(k * sizeZ + sizeZ / 2);
						spawnX = playerX;
						spawnY = playerY;
						spawnZ = playerZ;
						playerCollisionBox.move(-playerX, -playerY, -playerZ);
					}
					else if(mi.getObject(j, k) instanceof BlockType.HalfBlock)
					{
						Prism prism = new Prism(j * sizeX, i * sizeY, k * sizeZ, sizeX, sizeY, sizeZ, false);
						PrismImporter pi = new PrismImporter("halfBlock", prism);
						DisplayListHelper.dispForPrism(pi);
						mapCollision.add(pi.getPrism(false));
					}
					else if(mi.getObject(j, k) instanceof BlockType.BallFlag)
					{
						balls.add((BlockType.BallFlag) mi.getObject(j, k));
						balls.get(balls.size() - 1).setPosition(j * sizeX + sizeX / 2, i * sizeY + sizeY / 2, k * sizeZ + sizeZ / 2);
					}
				}
			}
		}
		DisplayListHelper.endList();
	}
}